<!DOCTYPE HTML>
<html lang="zh-CN">


<head>
    <meta charset="utf-8">
    <meta name="keywords" content="第12章 函数式编程： 匿名函数、高阶函数、装饰器, 湮灭星空,博客,Python,后端">
    <meta name="description" content="本章节进一步介绍函数式编程的lambda、mapeduce、filter以及装饰器

12-1 lambda表达式
lambda表达式又被称为匿名函数。

匿名函数：我们在定义一个函数的时候，不需要定义它的函数名。

lambda para">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
    <meta name="renderer" content="webkit|ie-stand|ie-comp">
    <meta name="mobile-web-app-capable" content="yes">
    <meta name="format-detection" content="telephone=no">
    <meta name="apple-mobile-web-app-capable" content="yes">
    <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent">
    <title>第12章 函数式编程： 匿名函数、高阶函数、装饰器 | 湮灭星空</title>
    <link rel="icon" type="image/png" href="/favicon.png">

    <link rel="stylesheet" type="text/css" href="/libs/awesome/css/all.css">
    <link rel="stylesheet" type="text/css" href="/libs/materialize/materialize.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/aos/aos.css">
    <link rel="stylesheet" type="text/css" href="/libs/animate/animate.min.css">
    <link rel="stylesheet" type="text/css" href="/libs/lightGallery/css/lightgallery.min.css">
    <link rel="stylesheet" type="text/css" href="/css/matery.css">
    <link rel="stylesheet" type="text/css" href="/css/my.css">

    <script src="/libs/jquery/jquery.min.js"></script>

<link rel="alternate" href="/atom.xml" title="湮灭星空" type="application/atom+xml">
</head>

<body>
    <header class="navbar-fixed">
    <nav id="headNav" class="bg-color nav-transparent">
        <div id="navContainer" class="nav-wrapper head-container">
            <div class="brand-logo">
                <a href="/" class="waves-effect waves-light">
                    
                    <img src="/medias/logo.png" class="logo-img" alt="LOGO">
                    
                    <span class="logo-span">湮灭星空</span>
                </a>
            </div>
            

<a href="#" data-target="mobile-nav" class="sidenav-trigger button-collapse"><i class="fas fa-bars"></i></a>
<ul class="right nav-menu">
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/" class="waves-effect waves-light">
      
      <i class="fas fa-home" style="zoom: 0.6;"></i>
      
      <span>首页</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/tags" class="waves-effect waves-light">
      
      <i class="fas fa-tags" style="zoom: 0.6;"></i>
      
      <span>标签</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/categories" class="waves-effect waves-light">
      
      <i class="fas fa-bookmark" style="zoom: 0.6;"></i>
      
      <span>分类</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/archives" class="waves-effect waves-light">
      
      <i class="fas fa-archive" style="zoom: 0.6;"></i>
      
      <span>归档</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/about" class="waves-effect waves-light">
      
      <i class="fas fa-user-circle" style="zoom: 0.6;"></i>
      
      <span>关于</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/contact" class="waves-effect waves-light">
      
      <i class="fas fa-comments" style="zoom: 0.6;"></i>
      
      <span>留言板</span>
    </a>
    
  </li>
  
  <li class="hide-on-med-and-down nav-item">
    
    <a href="/friends" class="waves-effect waves-light">
      
      <i class="fas fa-address-book" style="zoom: 0.6;"></i>
      
      <span>友链</span>
    </a>
    
  </li>
  
  <li>
    <a href="#searchModal" class="modal-trigger waves-effect waves-light">
      <i id="searchIcon" class="fas fa-search" title="搜索" style="zoom: 0.85;"></i>
    </a>
  </li>
</ul>

<div id="mobile-nav" class="side-nav sidenav">

    <div class="mobile-head bg-color">
        
        <img src="/medias/logo.png" class="logo-img circle responsive-img">
        
        <div class="logo-name">湮灭星空</div>
        <div class="logo-desc">
            
            天下寥寥，苍生涂涂，诸子百家，唯我纵横
            
        </div>
    </div>

    

    <ul class="menu-list mobile-menu-list">
        
        <li class="m-nav-item">
	  
		<a href="/" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-home"></i>
			
			首页
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/tags" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-tags"></i>
			
			标签
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/categories" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-bookmark"></i>
			
			分类
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/archives" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-archive"></i>
			
			归档
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/about" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-user-circle"></i>
			
			关于
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/contact" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-comments"></i>
			
			留言板
		</a>
          
        </li>
        
        <li class="m-nav-item">
	  
		<a href="/friends" class="waves-effect waves-light">
			
			    <i class="fa-fw fas fa-address-book"></i>
			
			友链
		</a>
          
        </li>
        
        
    </ul>
</div>

        </div>

        
    </nav>

</header>

    



<div class="bg-cover pd-header post-cover" style="background-image: url('/medias/featureimages/19.jpg')">
    <div class="container" style="right: 0px;left: 0px;">
        <div class="row">
            <div class="col s12 m12 l12">
                <div class="brand">
                    <div class="description center-align post-title">
                        第12章 函数式编程： 匿名函数、高阶函数、装饰器
                    </div>
                </div>
            </div>
        </div>
    </div>
</div>




<main class="post-container content">

    
    <link rel="stylesheet" href="/libs/tocbot/tocbot.css">
<style>
    #articleContent h1::before,
    #articleContent h2::before,
    #articleContent h3::before,
    #articleContent h4::before,
    #articleContent h5::before,
    #articleContent h6::before {
        display: block;
        content: " ";
        height: 100px;
        margin-top: -100px;
        visibility: hidden;
    }

    #articleContent :focus {
        outline: none;
    }

    .toc-fixed {
        position: fixed;
        top: 64px;
    }

    .toc-widget {
        padding-left: 20px;
    }

    .toc-widget .toc-title {
        margin: 35px 0 15px 0;
        padding-left: 17px;
        font-size: 1.5rem;
        font-weight: bold;
        line-height: 1.5rem;
    }

    .toc-widget ol {
        padding: 0;
        list-style: none;
    }

    #toc-content ol {
        padding-left: 10px;
    }

    #toc-content ol li {
        padding-left: 10px;
    }

    #toc-content .toc-link:hover {
        color: #42b983;
        font-weight: 700;
        text-decoration: underline;
    }

    #toc-content .toc-link::before {
        background-color: transparent;
        max-height: 25px;
    }

    #toc-content .is-active-link {
        color: #42b983;
    }

    #toc-content .is-active-link::before {
        background-color: #42b983;
    }

    #floating-toc-btn {
        position: fixed;
        right: 15px;
        bottom: 76px;
        padding-top: 15px;
        margin-bottom: 0;
        z-index: 998;
    }

    #floating-toc-btn .btn-floating {
        width: 48px;
        height: 48px;
    }

    #floating-toc-btn .btn-floating i {
        line-height: 48px;
        font-size: 1.4rem;
    }
</style>
<div class="row">
    <div id="main-content" class="col s12 m12 l9">
        <!-- 文章内容详情 -->
<div id="artDetail">
    <div class="card">
        <div class="card-content article-info">
            <div class="row tag-cate">
                <div class="col s7">
                    
                    <div class="article-tag">
                        
                            <a href="/tags/python/">
                                <span class="chip bg-color">python</span>
                            </a>
                        
                    </div>
                    
                </div>
                <div class="col s5 right-align">
                    
                    <div class="post-cate">
                        <i class="fas fa-bookmark fa-fw icon-category"></i>
                        
                            <a href="/categories/%E5%85%A5%E9%97%A8%E4%B8%8E%E8%BF%9B%E9%98%B6/" class="post-category">
                                入门与进阶
                            </a>
                        
                    </div>
                    
                </div>
            </div>

            <div class="post-info">
                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-minus fa-fw"></i>发布日期:&nbsp;&nbsp;
                    2019-03-14
                </div>
                

                
                <div class="post-date info-break-policy">
                    <i class="far fa-calendar-check fa-fw"></i>更新日期:&nbsp;&nbsp;
                    2019-12-06
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-file-word fa-fw"></i>文章字数:&nbsp;&nbsp;
                    6k
                </div>
                

                
                <div class="info-break-policy">
                    <i class="far fa-clock fa-fw"></i>阅读时长:&nbsp;&nbsp;
                    23 分
                </div>
                
				
                
                    <div id="busuanzi_container_page_pv" class="info-break-policy">
                        <i class="far fa-eye fa-fw"></i>阅读次数:&nbsp;&nbsp;
                        <span id="busuanzi_value_page_pv"></span>
                    </div>
				
            </div>
            
        </div>
        <hr class="clearfix">
        <div class="card-content article-card-content">
            <div id="articleContent">
                <p>本章节进一步介绍函数式编程的lambda、mapeduce、filter以及装饰器</p>
<a id="more"></a>
<h3 id="12-1-lambda表达式"><a class="header-anchor" href="#12-1-lambda表达式"></a>12-1 lambda表达式</h3>
<p>lambda表达式又被称为匿名函数。</p>
<blockquote>
<p>匿名函数：我们在定义一个函数的时候，不需要定义它的函数名。</p>
</blockquote>
<pre><code class="language-python">lambda parameter_list: expression
</code></pre>
<p>注意：</p>
<p>冒号后面的表达式只是表达式，不能赋值</p>
<pre><code class="language-python">def add(x, y):
    return x + y


var = lambda x, y: x + y

print(add(1, 2))
print(var(1, 2))
---------------------------
3
3
</code></pre>
<h3 id="12-2-三元表达式"><a class="header-anchor" href="#12-2-三元表达式"></a>12-2 三元表达式</h3>
<p>其他语言通用的三元表达式：<code>判断条件？ 条件为真时的返回结果 条件为假时的返回结果</code></p>
<p><code>Python</code>三元表达式的伪代码：</p>
<pre><code class="language-python">条件为真时的返回结果 if 判断条件 else 条件为假时的返回结果
</code></pre>
<p>示例：对于 x，y 如果 x &gt; y 返回 x，否则返回 y</p>
<pre><code class="language-python"># 其他语言
x &gt; y ？ x : y

# Python
x if x &gt; y else y
</code></pre>
<h3 id="12-3-map"><a class="header-anchor" href="#12-3-map"></a>12-3 map</h3>
<p><img src="https://klause-blog-pictures.oss-cn-shanghai.aliyuncs.com/2019-05-14-144421.jpg" alt="image-20181030192652350"></p>
<p><code>map</code>会将一个函数映射到一个输入列表的所有元素上。</p>
<p><code>map</code>接收两个参数：</p>
<ul>
<li>函数</li>
<li>序列</li>
</ul>
<pre><code class="language-python">map(function_to_apply, *list_of_inputs)
</code></pre>
<p>示例：</p>
<pre><code class="language-python">list_x = [1, 2, 3, 4, 5, 6, 7, 8]


def square(x):
    return x * x


# for item in list_x:
#     square(item)

r = map(square, list_x)
print(type(r), r)
print(list(r))
---------------------------------------
&lt;class 'map'&gt; &lt;map object at 0x10d54f4e0&gt;
[1, 4, 9, 16, 25, 36, 49, 64]
</code></pre>
<p>代码解释：</p>
<p><code>map</code>会对其所传入的集合或者序列的每一项执行 <code>square</code>函数，并且接收 <code>square</code>的返回结果。</p>
<p>如果我们从比较抽象的层面或者数学的层面来看它的作用呢，它其实就是一个映射。它可以把原集合的每一个元素映射到新集合里面来。至于说最终映射的结果是什么？是通过你编写的函数来决定的。</p>
<h3 id="12-4-map与lambda"><a class="header-anchor" href="#12-4-map与lambda"></a>12-4 map与lambda</h3>
<p><code>map</code>与 <code>lambda</code>表达式结合使用要比 <code>for</code>循环更加易懂、可阅读性更强、更精简、更优美。</p>
<pre><code class="language-python">list_x = [1, 2, 3, 4, 5, 6, 7, 8]
r = map(lambda x: x*x, list_x)

print(type(r), r)
print(list(r))
-------------------------------------
&lt;class 'map'&gt; &lt;map object at 0x10686c550&gt;
[1, 4, 9, 16, 25, 36, 49, 64]
</code></pre>
<h4 id="map-接收多参数"><a class="header-anchor" href="#map-接收多参数"></a>map 接收多参数</h4>
<ul>
<li><code>map</code>参数列表的数目必须与 <code>lambda</code>表达式数目相同；</li>
<li>结果列表的元素个数取决于，原始列表中较小列表的长度。</li>
</ul>
<pre><code class="language-python">list_x = [1, 2, 3, 4, 5, 6, 7, 8]
list_y = [1, 2, 3, 4, 5, 6]
r = map(lambda x, y: x*x + y, list_x, list_y)

print(type(r), r)
print(list(r))
------------------------------------------
&lt;class 'map'&gt; &lt;map object at 0x1065b3588&gt;
[2, 6, 12, 20, 30, 42]
</code></pre>
<blockquote>
<p>七月心语：</p>
<p><code>map</code>或者 <code>lambda</code>表达式并不能提升代码的运行效率，只能提升代码的简洁性。</p>
</blockquote>
<h3 id="12-5-reduce"><a class="header-anchor" href="#12-5-reduce"></a>12-5 reduce</h3>
<blockquote>
<p><code>map</code>在 vscode 的智能感知的情况下是可以直接看到 <code>map</code>的定义的，而 <code>reduce</code>却不可以，智能感知无法识别 <code>reduce</code>。那是因为 <code>Python3</code>里<code>reduce</code>已经不再<strong>全局的命名空间里</strong>了，我们需要先导入 <code>reduce</code>：</p>
<pre><code class="language-python">from functools import reduce
</code></pre>
</blockquote>
<p><img src="https://klause-blog-pictures.oss-cn-shanghai.aliyuncs.com/2019-05-14-144422.jpg" alt="image-20181030192612066"></p>
<p><code>reduce</code> 函数会对参数序列中元素进行累积。</p>
<p>函数将一个数据集合（链表，元组等）中的所有数据进行下列操作：用传给 reduce 中的函数 function（有两个参数）先对集合中的第 1、2 个元素进行操作，得到的结果再与第三个数据用 function 函数运算，最后得到一个结果。</p>
<p>示例：（第三个参数表示初始化，将初始值带入运算）</p>
<pre><code class="language-python">from functools import reduce

list_x = [1, 2, 3, 4, 5, 6, 7, 8]
r1 = reduce(lambda x, y: x + y, list_x, 10)
print(r1)

list_y = ['1', '2', '3', '4', '5', '6', '7', '8']
r2 = reduce(lambda x, y: x + y, list_y, 'aaa')
print(r2)
-----------------------------------------------
46
aaa12345678
</code></pre>
<p><code>reduce</code>是将执行流程高度抽象化了。</p>
<blockquote>
<p>七月心语：</p>
<p>如果对大数据有一定了解的同学，应听说过Google 提出过一个大数据的计算模型，这个计算模型就叫做 <strong>map/reduce</strong> ，这个map/reduce在大数据计算里是一个编程模型，那么 map 在这里面就是<strong>映射</strong>，reduce 在大数据里面叫做<strong>归约</strong>。map/reduce 在大数据里面最主要的应用是做并行计算。其实大数据里面的 map/reduce 借鉴的思想就是函数式编程。在 Python 里函数式编程最主要的体现就是在 map/reduce 上面。</p>
<p>Python 里面出现了 map/reduce，hdoop 里面也出现了 map/reduce，它们有什么区别呢？</p>
<p>区别肯定是有的，但是本质上来讲都是编程模型或者说是一种思想，都源自于函数式编程。</p>
</blockquote>
<h3 id="12-6-filter"><a class="header-anchor" href="#12-6-filter"></a>12-6 filter</h3>
<p><img src="https://klause-blog-pictures.oss-cn-shanghai.aliyuncs.com/2019-05-14-144424.jpg" alt="image-20181031091044590"></p>
<pre><code class="language-python">list_x = [0, 1, 0, 1, 1, 0]
r = filter(lambda x: True if x == 1 else False, list_x)
# r = filter(lambda x: x, list_x)
print(type(r), r)
print(list(r))
--------------------------------------------------------
&lt;class 'filter'&gt; &lt;filter object at 0x10ce8a5f8&gt;
[1, 1, 1]
</code></pre>
<h4 id="filter-的特点"><a class="header-anchor" href="#filter-的特点"></a>filter 的特点</h4>
<p><code>filter</code>要求第一个参数 <code>function</code>返回的必须是可以代表 <code>True</code>或者 <code>False</code>的类型。因为 filter 就是靠函数的返回结果来判断当前元素是否应该保留在结果集合中。如果返回的是 <code>False</code>，那么该元素将被剔除。这就是过滤器的作用。</p>
<p>示例：返回列表字符中的小写字母</p>
<pre><code class="language-python">import re

list_x = ['a', 'B', 'c', 'D']
r = filter(lambda x: True if re.findall('[a-z]', x) else False, list_x)
# r = filter(lambda x: re.findall('[a-z]', x), list_x)
print(list(r))
-------------------------------------------------------------------------
['a', 'c']
</code></pre>
<h3 id="12-7-命令式编程vs函数式编程"><a class="header-anchor" href="#12-7-命令式编程vs函数式编程"></a>12-7 命令式编程vs函数式编程</h3>
<p>我们现在最为熟知的其实还是命令式编程，但是我们在本小节之前学的 lambda、map、reduce、filter ，它们的实质应该是属于函数式编程。</p>
<h4 id="命令式编程"><a class="header-anchor" href="#命令式编程"></a>命令式编程</h4>
<p>命令式编程就是我们每天在写的代码的风格，或者说是一个过程的控制。那么我们可以想一想，无论多么复杂的业务逻辑，如果我们要控制它的流程，几个关键点是我们一定要有的：</p>
<ol>
<li>函数 def</li>
<li>条件判断 if else</li>
<li>循环 for</li>
</ol>
<p>我们可以发现不管多复杂的业务，我们可以通过 def、if else、for，都是可以去实现的。当然这里的总结并不完全，很多同学可能会提到，老师你不是还有包和模块吗？不是还有类和对象吗？是的，还有这些东西但是我们先挑选这些代表流程的东西来举例说明。我们关注的是命令式编程的本质，所以先不考虑这么多因素。举个例子来说，你没有这些功能可以实现这些功能吗？可以，函数可以实现所有的功能，你不把它封装成类和对象，直接写在函数里就行了。这三大类算是命令式编程最显著的特征了。</p>
<h4 id="函数式编程"><a class="header-anchor" href="#函数式编程"></a>函数式编程</h4>
<p>对应于命令式编程，函数式编程也有必须的一些关键字。我们常用的函数式编程的关键字或者说手段有4个：</p>
<ol>
<li>map</li>
<li>reduce</li>
<li>filter</li>
<li>lambda 表达式：算子</li>
</ol>
<p>理论上而言我们可以使用以上这四个去替换所有的命令式编程里复杂的业务逻辑。lambda 表达式其实是作为函数式编程最基本的单元，它在一定程度上是可以替换命令式编程里的函数的。其次是 map、reduce 其实是可以替换循环的。为什么说是理论上可以去实现呢？因为我也没有尝试过把很复杂的用命令式编程编写的项目全部改写成函数式编程。问题在于什么呢？在于我们习惯的就是命令式编程。我们上学的时候，包括我们看编程的书籍、视频，我们所有接受的都是命令式编程的思想，这个思想一旦形成之后你就很难去改变。这里只是一个简单的介绍，函数式编程可以说是一个全新的领域。</p>
<p>所以说我的建议就是：函数式编程，我们可以把它结合到我们最熟悉的命令式编程里面，来帮助我们写出一些简洁的代码，但是我不推荐大家全盘的使用函数式编程的思想来构建你的业务逻辑或者是实际项目。</p>
<p><code>lisp</code>语言算是函数式编程的鼻祖，如果你真的想用函数式编程的话，那么你就用 <code>lisp</code>。你不要强行拿 Java、python、c#、c++ 这些命令式的或者说是结构式的编程语言来强行写函数式编程。因为我强调一个观点：python、Java、c# 等，它们可能对函数式编程有一定支持的，但是这只是说它们支持部分特性，但是这些语言的本质来说还是命令式的编程，所以你千万不跟别人说 python 是函数式编程。</p>
<blockquote>
<p>这节课的目的并不是倡导到家用函数式编程去写代码，而是只是想让大家了解命令式编程和函数式编程到底是什么，以及他们有什么区别，我们达到这个目的就可以了。</p>
<p>我记得有同学之前问我，老师你之前学过 lisp 语言吗？我记得我上大学的时候在图书馆的角落里翻出一本 lisp 书籍，当时坐着翻了一会儿，说真的看的不是太懂。有兴趣的小伙伴可以去挑战一下，lisp 在人工智能的领域里也是用的比较多的。</p>
</blockquote>
<h3 id="12-8-装饰器-一"><a class="header-anchor" href="#12-8-装饰器-一"></a>12-8 装饰器 一</h3>
<h4 id="装饰器的写法"><a class="header-anchor" href="#装饰器的写法"></a>装饰器的写法</h4>
<p>装饰器必须满足三个条件：</p>
<ol>
<li>必须是嵌套函数</li>
<li>必须返回嵌套函数</li>
<li>调用的时候使用<code>@</code>语法糖</li>
</ol>
<blockquote>
<p>我个人认为学习装饰器，直接给出装饰器的概念或者说意义，我觉得是没有太大作用的。对于装饰器来讲，我们不仅要知道什么是装饰器，最关键的还是要理解装饰器的使用场景，以及装饰器可以帮助我们解决什么样的问题，它的优势在什么地方。搞清楚这些才算是真正理解了装饰器。</p>
</blockquote>
<h4 id="unix-时间戳"><a class="header-anchor" href="#unix-时间戳"></a>Unix 时间戳</h4>
<p>Unix 时间戳（Unix timestamp）或者成 Unix 时间（Unix time）或者POSIX 时间（POSIX time）是一种时间表示方式，定义为从格林威治时间1970年01月01日00分00秒起至现在的总秒数。</p>
<p>Python 获取 Unix 时间戳：</p>
<pre><code class="language-python">import time
time.time()
</code></pre>
<p>Python 将Unix 时间戳 转换为普通时间：</p>
<pre><code class="language-python">import time
time.gmtime(Unix timestamp)
time.strftime('%Y-%m-%d %H:%M:%S')
</code></pre>
<p>Python 将普通时间转换为 Unix 时间戳：</p>
<pre><code class="language-python">import time
int(time.mktime(time.strptime('YYYY-MM-DD HH:MM:SS', '%Y-%m-%d %H:%M:%S')))
</code></pre>
<p>示例：</p>
<pre><code class="language-python">import time


def f1():
    print(time.time())
    print(time.clock())
    print(time.gmtime())
    print(time.strftime('%Y-%m-%d %H:%M:%S'))
    print('This is a function.')

    new_time = int(time.mktime(time.strptime('2018-10-31 14:15:50', '%Y-%m-%d %H:%M:%S')))
    print(new_time)


f1()
-------------------------------------------------------------------------------
1540966577.973166
0.042615
time.struct_time(tm_year=2018, tm_mon=10, tm_mday=31, tm_hour=6, tm_min=16, tm_sec=17, tm_wday=2, tm_yday=304, tm_isdst=0)
2018-10-31 14:16:17
This is a function.
1540966550
</code></pre>
<h4 id="问题场景"><a class="header-anchor" href="#问题场景"></a>问题场景</h4>
<ol>
<li>
<p>需要在一个函数里打印程序执行时的时间</p>
<pre><code class="language-python">import time


def f1():
    print(time.strftime('%Y-%m-%d %H:%M:%S'))
    print('This is a function.')


f1()
---------------------------------------------
2018-10-31 14:21:58
This is a function.
</code></pre>
</li>
<li>
<p>假如我有1000个函数，都需要附带这种在内部打印执行时间的功能呢？</p>
</li>
<li>
<pre><code class="language-python">import time


def f1():
    print(time.strftime('%Y-%m-%d %H:%M:%S'))
    print('This is a function.')


def f2():
    print(time.strftime('%Y-%m-%d %H:%M:%S'))
    print('This is a function.')


def f3():
    print(time.strftime('%Y-%m-%d %H:%M:%S'))
    print('This is a function.')


...
f1()
f2()
f3()
---------------------------------------------
2018-10-31 14:24:31
This is a function.
2018-10-31 14:24:31
This is a function.
2018-10-31 14:24:31
This is a function.
</code></pre>
<p>像这么在每个函数的内部加上这样的语句显然是不切合实际的，而且也违背了开闭原则（对修改是关闭的，对扩展是开放的）。</p>
<p>那么有的同学就用函数式编程的方法，将函数作为参数，提出了下面的解决方案：</p>
<pre><code class="language-python">import time


def f1():
    print('This is a function.')


def f2():
    print('This is a function.')


def f3():
    print('This is a function.')


...


def print_current_time(func):
    print(time.strftime('%Y-%m-%d %H:%M:%S'))
    func()


print_current_time(f1)
print_current_time(f2)
print_current_time(f3)
-----------------------------------------------------
2018-10-31 14:30:36
This is a function.
2018-10-31 14:30:36
This is a function.
2018-10-31 14:30:36
This is a function.
</code></pre>
<p>这个方法的有点是准守了开闭原则。巧妙的运用了函数式编程的特性，将函数作为参数传递到另外的函数里面去，然后再另外的函数里面通过打印时间，最终完成了需求的变更。</p>
<p>如果不使用装饰器，那么像这样的新增加一个函数的方式不失为一种方法，但是这种方法我个人认为是不够好的，至少不够优雅。这种方案是优缺点的，很多课程或者教程在解释这样一种对于需求变更的处理时候的缺点呢，我觉得都没有说到关键的地方，关键在于打印时间这个新增加的需求，是属于每个函数本身的，并不是属于新增加的函数的。我们这样是相当于是强行的在每个函数之前加入了打印时间，它并没有体现函数本身的特性。其实上述写法和这种写法完全没有区别：</p>
<pre><code class="language-python">print(time.strftime('%Y-%m-%d %H:%M:%S'))
f1()
print(time.strftime('%Y-%m-%d %H:%M:%S'))
f2()
print(time.strftime('%Y-%m-%d %H:%M:%S'))
f3()
</code></pre>
</li>
<li>
<p>那么有没有一种方法，可以让我们新添加的业务逻辑依然可以和老函数绑定在一起，体现出对原来老函数功能的增加，但是又不去更改函数内部的实现呢？这个就是装饰器要去解决的问题。</p>
</li>
</ol>
<h3 id="12-9-装饰器-二"><a class="header-anchor" href="#12-9-装饰器-二"></a>12-9 装饰器 二</h3>
<pre><code class="language-python">import time


def decorator(func):
    def wrapper():
        print(time.strftime('%Y-%m-%d %H:%M:%S'))
        func()
    return wrapper


def f1():
    print('This is a function.')


f = decorator(f1)
f()
------------------------------------------------------
2018-10-31 15:26:01
This is a function.
</code></pre>
<p>上述写法就是装饰器的写法了，其实只是变了一个魔术，和上节中的调用方案没有太大的区别，只不过这样的写法内置性更高。</p>
<blockquote>
<p>注意：上述装饰器的写法与闭包的写法相似，只是没有引用外部的、非全局的环境变量。</p>
</blockquote>
<p>回到上一节所提出的问题，上节的解法里不能体现出新功能与 f1、f2 等函数的关联性，本节的这种所谓的装饰器的解法其实也没用体现出这种关联性。</p>
<p>下面我们来看下这样的写法为什么叫装饰器？</p>
<p>真正的业务逻辑其实不在 decorator 里面，真正的业务实际上在 wrapper 里面（wrapper 是被封装的意思）。也就是说我们通过在 wrapper 外面包裹了一层 decorator 来实现我们不改变原有函数，而增加了新的功能这样的一种特性。那么看起来就像 wrapper 被 decorator 装饰了一样，所以这也是它叫装饰器的原因。</p>
<p>不知道各位同学听明白了没有，反正我是没有听明白，我刚刚讲的对于装饰器的解释，是绝大多数课程或者教材或者是书籍里面所定义的装饰器，但是在我看来这样的解释根本没有体现出装饰的特性。那么下一节课给大家讲一讲我理解的装饰器为什么叫装饰器。</p>
<h3 id="12-10-装饰器-三"><a class="header-anchor" href="#12-10-装饰器-三"></a>12-10 装饰器 三</h3>
<h4 id="语法糖"><a class="header-anchor" href="#语法糖"></a>@ 语法糖</h4>
<p>使用 <code>@</code> 语法糖直接可以不改变原函数的调用方式，给原函数添加新的功能。相比于前两节所介绍的方法都必须改变原函数的调用方式才能打印出程序执行时的时间。</p>
<p><code>@</code>语法糖优势展露无遗。这就是装饰器最大的特点，也是其意义所在。</p>
<pre><code class="language-python">import time


def decorator(func):
    def wrapper():
        print(time.strftime('%Y-%m-%d %H:%M:%S'))
        func()
    return wrapper


# 调用语法糖
@decorator
def f1():
    print('This is a function.')


f1()
--------------------------------------------------
2018-10-31 15:45:23
This is a function.
</code></pre>
<p>这样看起来只有定义的时候比较复杂，调用的时候很方便。我们可以接受定义时候的复杂，但是我们决定不能接受调用时候的复杂，因为我们可能只需要写一次定义，但是我们可能会调用无数次，这也是我们封装的最基本的原则。</p>
<blockquote>
<p>七月心语：</p>
<p>Python 里的装饰器其实涉及到了 AOP 的编程思想，那么这个 AOP 就不展开讲了，这个涉及到编程模式，而且我比较推荐 AOP 的编程。我在上一门 PHP 的课程里面大量的应用了 AOP 的编程思想，想了解的同学建议私下了解一下。</p>
</blockquote>
<h3 id="12-11-装饰器-四"><a class="header-anchor" href="#12-11-装饰器-四"></a>12-11 装饰器 四</h3>
<h4 id="传递参数"><a class="header-anchor" href="#传递参数"></a>传递参数</h4>
<p>上节介绍了简单的装饰器，上节的装饰器是不需要任何参数的，如果我们所装饰的函数需要传入参数，这个装饰器需要做哪些改变才能支持带参数的函数呢？使用<code>*args</code>：表示参数列表。</p>
<blockquote>
<p>args：是通用命名。在具体函数里还是使用字面意识能够代表参数含义的参数名会比较好。</p>
</blockquote>
<pre><code class="language-python">import time


def decorator(func):
    def wrapper(*args):
        print(time.strftime('%Y-%m-%d %H:%M:%S'))
        func(*args)

    return wrapper


# 调用语法糖
@decorator
def f1(arg):
    print('This is a function: ' + arg)


@decorator
def f2(arg1, arg2):
    print('This is a function: ' + str(arg1))
    print('This is a function: ' + str(arg2))


test_function1 = 1
test_function2 = 2
f1('test')
f2(test_function1, test_function2)
-------------------------------------------------------
2018-10-31 17:01:46
This is a function: test
2018-10-31 17:01:46
This is a function: 1
This is a function: 2
</code></pre>
<h3 id="12-12-装饰器-五"><a class="header-anchor" href="#12-12-装饰器-五"></a>12-12 装饰器 五</h3>
<h4 id="传递关键字参数"><a class="header-anchor" href="#传递关键字参数"></a>传递关键字参数</h4>
<p>让装饰器兼容带有关键字参数的函数：使用<code>**kwargs</code>：表示关键字参数列表。</p>
<blockquote>
<p>kwargs：是通用命名，代表 key word args 关键字的意思。这个词可以任意命名。</p>
</blockquote>
<pre><code class="language-python">import time


def decorator(func):
    def wrapper(*args, **kwargs):
        print(time.strftime('%Y-%m-%d %H:%M:%S'))
        func(*args, **kwargs)

    return wrapper


# 调用语法糖
@decorator
def f1(arg):
    print('This is a function: ' + arg)


@decorator
def f2(arg1, arg2):
    print('This is a function: ' + str(arg1))
    print('This is a function: ' + str(arg2))


@decorator
def f3(arg1, arg2, **kw):
    print('This is a function: ' + str(arg1))
    print('This is a function: ' + str(arg2))
    print(kw)


test_function1 = 1
test_function2 = 2
f1('test')
f2(test_function1, test_function2)
f3(test_function1, test_function2, a=1, b=2, c='123')
-----------------------------------------------------------
2018-10-31 17:10:26
This is a function: test
2018-10-31 17:10:26
This is a function: 1
This is a function: 2
2018-10-31 17:10:26
This is a function: 1
This is a function: 2
{'a': 1, 'b': 2, 'c': '123'}
</code></pre>
<blockquote>
<p>小技巧：</p>
<p>在 Python 里<code>func(*args, **kwargs)</code>基本上是可以调用具有任何形式的参数（一个参数、两个参数、关键字参数等等）。</p>
<p>当你想调用一个函数，你又不知道这个函数是怎么定义的时候（不知道该函数需要什么参数），就可以使用<code>func(*args, **kwargs)</code>这种形式。</p>
</blockquote>
<h3 id="12-13-装饰器-六"><a class="header-anchor" href="#12-13-装饰器-六"></a>12-13 装饰器 六</h3>
<h4 id="代码稳定性"><a class="header-anchor" href="#代码稳定性"></a>代码稳定性</h4>
<p>装饰器最基本的思想在于如果我们想对某一个被封装的单元，比如说是函数，做出一个代码上的修改的话，我们可以不去改动单元或者函数内部具体的实现，而是通过装饰器的这样一种形式来改变这个函数的行为，这个是从代码的稳定性的角度阐述装饰器的作用。</p>
<h4 id="代码复用性"><a class="header-anchor" href="#代码复用性"></a>代码复用性</h4>
<p>从代码复用性的角度阐述装饰器的用法：</p>
<p>比如我们打印程序运行时的时间的功能就是新增的一个逻辑，那么我们现在把这个功能封装成一个装饰器，凡是需要有打印时间这个功能的函数都可以通过装饰器的<code>@</code>语法糖将这个新的功能加在函数上面，这个其实代码的复用。</p>
<h4 id="web-框架举例"><a class="header-anchor" href="#web-框架举例"></a>web 框架举例</h4>
<p>下面举几个实际的例子，这些例子都是框架里一些特有的功能：</p>
<p><img src="https://klause-blog-pictures.oss-cn-shanghai.aliyuncs.com/2019-05-14-144426.jpg" alt="image-20181031182328186"></p>
<p>在 <code>flask</code>内部就是用了大量的装饰器。上图展示 <code>flask</code> 里面的两个控制器。<code>flask</code>是我经常用的 web 框架，如果我们要开发 web，<code>flask</code>是个比较好的选择。我们可以看到在<code>flask</code>控制器上面有<code>@</code>符号，这个就是一个装饰器。我们先来看下<code>@api.route</code>，如果我们不添加<code>@api.route</code>的话，它只是一个简单的函数，当我们加了<code>@api.route</code>之后，这个<code>test_javascript_http</code>它就可以变成一个控制器了。这个控制器的作用是可以将访问这个控制器的 <code>url</code>地址设置成 <code>get</code>，还可以指定访问这个控制器的 <code>http</code>方法。关键在于，如果这个装饰器只是用在这一个控制器上面的话就没什么意义，但是我们可以看到我们还可以将装饰器用在别的函数上，凡是加了<code>@api.route</code>的函数，它马上就不是普通函数了，而是变成了一个控制器。</p>
<p>如果大家觉得这个装饰器还没有体现出它的优势来，我告诉大家<code>@api.route</code>这个装饰器内部的代码是很复杂的。大家可以想一想，这么复杂的代码，如果不用装饰器来实现，而是直接把复杂的代码加到函数里，大家觉得这样可行吗？加一个可能没什么问题，那么加100个呢？大家都知道对于一个 web 项目来说，稍微中大型一点的，三五十个接口实在是太正常了，<code>rest</code>的接口可能少一点，如果不是 <code>rest</code>的话，如果你直接是业务型的接口，成百上千个实在是太正常了。你把这样很复杂的逻辑平摊到每个函数里面显然是不可行的，这个时候装饰器的优势就体现出来了。我只在我需要的函数前加上装饰器就可以了，它就变成一个控制器了。这个是<code>@api.route</code>装饰器的作用。</p>
<p>我们再看下面<code>@auth.login_required</code>装饰器，正如我们所看到的，并不是一个函数只能增加一个装饰器，它可以增加很多个装饰器，你可以像盖楼房一样，一层一层的将这些装饰器磊在一起。那我们知道有些接口是面向大众的，谁都可以访问额，但是有些接口却不是任何人都可以访问的。比如我们所提供的接口是图书信息服务，这个图书信息服务我们可以让任何人都可以访问，比如说像豆瓣的开放 api，它的图书信息是谁都可以访问的，不需要登录也不需要注册，但是对于有一些接口的话，比如说我们要删除一个数据的话，这样的接口是谁都可以访问的吗？显然不是。所以说借口大致分为两类，一类是所有人都可以访问的，另一类是需要受保护的。那么具体到如果是我们自己来编写代码的时候，我们就需要做区分，比如说上面接口谁都可以访问，下面接口我不想让其他人访问，我需要验证它的身份，身份通过了之后我才能去让它访问我的接口，这个时候装饰器就是一个很灵活的用法。我可以在所有需要验证用户身份的接口上面加上<code>@auth.login_required</code>装饰器，那么这个函数就变成了一个需要验证才能访问的控制器。</p>
<p>就给大家举这两个例子，因为我自己是对 <code>flask</code>比较熟的，我自己写 <code>flask</code>的时候就会写很多装饰器。</p>
<p>装饰器还是体现了 AOP 的思想，也就是从一个横切面上解决问题，有基础的同学可以去了解一下。AOP 确实是很好用的思想。</p>

<div id="gitalk-container"></div>
<script src="https://cdn.bootcss.com/blueimp-md5/2.12.0/js/md5.min.js"></script><link rel="stylesheet" href="https://unpkg.com/gitalk/dist/gitalk.css"><script src="https://unpkg.com/gitalk/dist/gitalk.min.js"></script>

		<script>
		var gitalkConfig = {"clientID":"9eb5bc3ac1e1ff3ddac0","clientSecret":"4b7ae28042282281295075c2bf0c97ff1791cfeb","repo":"HexoBlogComments","owner":"Annihilater","admin":["Annihilater"],"distractionFreeMode":false};
	    gitalkConfig.id = md5(location.pathname);
		var gitalk = new Gitalk(gitalkConfig);
	    gitalk.render("gitalk-container");
	    </script>
            </div>
            <hr/>

            

    <div class="reprint" id="reprint-statement">
        
            <div class="reprint__author">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-user">
                        文章作者:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://www.klause.cn" rel="external nofollow noreferrer">湮灭星空</a>
                </span>
            </div>
            <div class="reprint__type">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-link">
                        文章链接:
                    </i>
                </span>
                <span class="reprint-info">
                    <a href="https://www.klause.cn/2019/03/14/python3-ru-men-yu-jin-jie-shi-pin-bi-ji/di-12-zhang-han-shu-shi-bian-cheng-ni-ming-han-shu-gao-jie-han-shu-zhuang-shi-qi/">https://www.klause.cn/2019/03/14/python3-ru-men-yu-jin-jie-shi-pin-bi-ji/di-12-zhang-han-shu-shi-bian-cheng-ni-ming-han-shu-gao-jie-han-shu-zhuang-shi-qi/</a>
                </span>
            </div>
            <div class="reprint__notice">
                <span class="reprint-meta" style="font-weight: bold;">
                    <i class="fas fa-copyright">
                        版权声明:
                    </i>
                </span>
                <span class="reprint-info">
                    本博客所有文章除特別声明外，均采用
                    <a href="https://creativecommons.org/licenses/by/4.0/deed.zh" rel="external nofollow noreferrer" target="_blank">CC BY 4.0</a>
                    许可协议。转载请注明来源
                    <a href="https://www.klause.cn" target="_blank">湮灭星空</a>
                    !
                </span>
            </div>
        
    </div>

    <script async defer>
      document.addEventListener("copy", function (e) {
        let toastHTML = '<span>复制成功，请遵循本文的转载规则</span><button class="btn-flat toast-action" onclick="navToReprintStatement()" style="font-size: smaller">查看</a>';
        M.toast({html: toastHTML})
      });

      function navToReprintStatement() {
        $("html, body").animate({scrollTop: $("#reprint-statement").offset().top - 80}, 800);
      }
    </script>



            <div class="tag_share" style="display: block;">
                <div class="post-meta__tag-list" style="display: inline-block;">
                    
                        <div class="article-tag">
                            
                                <a href="/tags/python/">
                                    <span class="chip bg-color">python</span>
                                </a>
                            
                        </div>
                    
                </div>
                <div class="post_share" style="zoom: 80%; width: fit-content; display: inline-block; float: right; margin: -0.15rem 0;">
                    <link rel="stylesheet" type="text/css" href="/libs/share/css/share.min.css">

<div id="article-share">
    
    
    <div class="social-share" data-sites="twitter,facebook,google,qq,qzone,wechat,weibo,douban,linkedin" data-wechat-qrcode-helper="<p>微信扫一扫即可分享！</p>"></div>
    <script src="/libs/share/js/social-share.min.js"></script>
    

    

</div>

                </div>
            </div>
            
        </div>
    </div>

    

    

    

    

    

    

<article id="prenext-posts" class="prev-next articles">
    <div class="row article-row">
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge left-badge text-color">
                <i class="fas fa-chevron-left"></i>&nbsp;上一篇</div>
            <div class="card">
                <a href="/2019/03/26/markdown/sites-available-default/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/14.jpg" class="responsive-img" alt="sites-available_default">
                        
                        <span class="card-title">sites-available_default</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            这里是/etc/nginx/sites-available/default文件的原版！
# /etc/nginx/sites-available/default
##
# You should look at the following U
                        
                    </div>
                    <div class="publish-info">
                        <span class="publish-date">
                            <i class="far fa-clock fa-fw icon-date"></i>2019-03-26
                        </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/Tech/" class="post-category">
                                    Tech
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/markdown/">
                        <span class="chip bg-color">markdown</span>
                    </a>
                    
                    <a href="/tags/nginx/">
                        <span class="chip bg-color">nginx</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
        
        <div class="article col s12 m6" data-aos="fade-up">
            <div class="article-badge right-badge text-color">
                下一篇&nbsp;<i class="fas fa-chevron-right"></i>
            </div>
            <div class="card">
                <a href="/2019/03/14/python3-ru-men-yu-jin-jie-shi-pin-bi-ji/di-11-zhang-python-de-gao-ji-yu-fa-yu-yong-fa/">
                    <div class="card-image">
                        
                        
                        <img src="/medias/featureimages/0.jpg" class="responsive-img" alt="第11章 Python的高级语法与用法">
                        
                        <span class="card-title">第11章 Python的高级语法与用法</span>
                    </div>
                </a>
                <div class="card-content article-content">
                    <div class="summary block-with-text">
                        
                            本章节我们揭开Python进阶部分的高级特性，详细讲解枚举、闭包，并对函数式编程做出介绍。

11-1 枚举其实是一个类
枚举可以用来表示不同的类型。将一个类继承 Enum，它就会变成一个枚举类。

枚举的标签和数值是一对一的关系

fro
                        
                    </div>
                    <div class="publish-info">
                            <span class="publish-date">
                                <i class="far fa-clock fa-fw icon-date"></i>2019-03-14
                            </span>
                        <span class="publish-author">
                            
                            <i class="fas fa-bookmark fa-fw icon-category"></i>
                            
                            <a href="/categories/%E5%85%A5%E9%97%A8%E4%B8%8E%E8%BF%9B%E9%98%B6/" class="post-category">
                                    入门与进阶
                                </a>
                            
                            
                        </span>
                    </div>
                </div>
                
                <div class="card-action article-tags">
                    
                    <a href="/tags/python/">
                        <span class="chip bg-color">python</span>
                    </a>
                    
                </div>
                
            </div>
        </div>
        
    </div>
</article>

</div>



<!-- 代码块功能依赖 -->
<script type="text/javascript" src="/libs/codeBlock/codeBlockFuction.js"></script>

<!-- 代码语言 -->

<script type="text/javascript" src="/libs/codeBlock/codeLang.js"></script>

    
<!-- 代码块复制 -->

<script type="text/javascript" src="/libs/codeBlock/codeCopy.js"></script>


<!-- 代码块收缩 -->

<script type="text/javascript" src="/libs/codeBlock/codeShrink.js"></script>


<!-- 代码块折行 -->

<style type="text/css">
code[class*="language-"], pre[class*="language-"] { white-space: pre !important; }
</style>

    </div>
    <div id="toc-aside" class="expanded col l3 hide-on-med-and-down">
        <div class="toc-widget">
            <div class="toc-title"><i class="far fa-list-alt"></i>&nbsp;&nbsp;目录</div>
            <div id="toc-content"></div>
        </div>
    </div>
</div>

<!-- TOC 悬浮按钮. -->

<div id="floating-toc-btn" class="hide-on-med-and-down">
    <a class="btn-floating btn-large bg-color">
        <i class="fas fa-list-ul"></i>
    </a>
</div>


<script src="/libs/tocbot/tocbot.min.js"></script>
<script>
    $(function () {
        tocbot.init({
            tocSelector: '#toc-content',
            contentSelector: '#articleContent',
            headingsOffset: -($(window).height() * 0.4 - 45),
            // headingsOffset: -205,
            headingSelector: 'h2, h3, h4, h5, h6'
        });

        // modify the toc link href to support Chinese.
        let i = 0;
        let tocHeading = 'toc-heading-';
        $('#toc-content a').each(function () {
            $(this).attr('href', '#' + tocHeading + (++i));
        });

        // modify the heading title id to support Chinese.
        i = 0;
        $('#articleContent').children('h2, h3, h4, h5, h6').each(function () {
            $(this).attr('id', tocHeading + (++i));
        });

        // Set scroll toc fixed.
        let tocHeight = parseInt($(window).height() * 0.4 - 64);
        let $tocWidget = $('.toc-widget');
        $(window).scroll(function () {
            let scroll = $(window).scrollTop();
            /* add post toc fixed. */
            if (scroll > tocHeight) {
                $tocWidget.addClass('toc-fixed');
            } else {
                $tocWidget.removeClass('toc-fixed');
            }
        });

        
        /* 修复文章卡片 div 的宽度. */
        let fixPostCardWidth = function (srcId, targetId) {
            let srcDiv = $('#' + srcId);
            if (srcDiv.length === 0) {
                return;
            }

            let w = srcDiv.width();
            if (w >= 450) {
                w = w + 21;
            } else if (w >= 350 && w < 450) {
                w = w + 18;
            } else if (w >= 300 && w < 350) {
                w = w + 16;
            } else {
                w = w + 14;
            }
            $('#' + targetId).width(w);
        };

        // 切换TOC目录展开收缩的相关操作.
        const expandedClass = 'expanded';
        let $tocAside = $('#toc-aside');
        let $mainContent = $('#main-content');
        $('#floating-toc-btn .btn-floating').click(function () {
            if ($tocAside.hasClass(expandedClass)) {
                $tocAside.removeClass(expandedClass).hide();
                $mainContent.removeClass('l9');
            } else {
                $tocAside.addClass(expandedClass).show();
                $mainContent.addClass('l9');
            }
            fixPostCardWidth('artDetail', 'prenext-posts');
        });
        
    });
</script>

    

</main>



    <footer class="page-footer bg-color">
    <div class="container row center-align">
        <div class="col s12 m8 l8 copy-right">
            Copyright&nbsp;&copy;
            <span id="year">年份</span>
            <a href="https://www.klause.cn" target="_blank">湮灭星空</a>
            |&nbsp;Powered by&nbsp;<a href="https://hexo.io/" target="_blank">Hexo</a>
            |&nbsp;Theme&nbsp;<a href="https://github.com/blinkfox/hexo-theme-matery" target="_blank">Matery</a>
            <br>
            
            &nbsp;<i class="fas fa-chart-area"></i>&nbsp;站点总字数:&nbsp;<span
                class="white-color">199.2k</span>&nbsp;字
            
            
            
            
            
            
            <span id="busuanzi_container_site_pv">
                |&nbsp;<i class="far fa-eye"></i>&nbsp;总访问量:&nbsp;<span id="busuanzi_value_site_pv"
                    class="white-color"></span>&nbsp;次
            </span>
            
            
            <span id="busuanzi_container_site_uv">
                |&nbsp;<i class="fas fa-users"></i>&nbsp;总访问人数:&nbsp;<span id="busuanzi_value_site_uv"
                    class="white-color"></span>&nbsp;人
            </span>
            
            <br>
            
            <span id="sitetime">载入运行时间...</span>
            <script>
                function siteTime() {
                    window.setTimeout("siteTime()", 1000);
                    var seconds = 1000;
                    var minutes = seconds * 60;
                    var hours = minutes * 60;
                    var days = hours * 24;
                    var years = days * 365;
                    var today = new Date();
                    var startYear = "2019";
                    var startMonth = "6";
                    var startDate = "28";
                    var startHour = "0";
                    var startMinute = "0";
                    var startSecond = "0";
                    var todayYear = today.getFullYear();
                    var todayMonth = today.getMonth() + 1;
                    var todayDate = today.getDate();
                    var todayHour = today.getHours();
                    var todayMinute = today.getMinutes();
                    var todaySecond = today.getSeconds();
                    var t1 = Date.UTC(startYear, startMonth, startDate, startHour, startMinute, startSecond);
                    var t2 = Date.UTC(todayYear, todayMonth, todayDate, todayHour, todayMinute, todaySecond);
                    var diff = t2 - t1;
                    var diffYears = Math.floor(diff / years);
                    var diffDays = Math.floor((diff / days) - diffYears * 365);
                    var diffHours = Math.floor((diff - (diffYears * 365 + diffDays) * days) / hours);
                    var diffMinutes = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours) /
                        minutes);
                    var diffSeconds = Math.floor((diff - (diffYears * 365 + diffDays) * days - diffHours * hours -
                        diffMinutes * minutes) / seconds);
                    if (startYear == todayYear) {
                        document.getElementById("year").innerHTML = todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffDays + " 天 " + diffHours +
                            " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    } else {
                        document.getElementById("year").innerHTML = startYear + " - " + todayYear;
                        document.getElementById("sitetime").innerHTML = "本站已安全运行 " + diffYears + " 年 " + diffDays +
                            " 天 " + diffHours + " 小时 " + diffMinutes + " 分钟 " + diffSeconds + " 秒";
                    }
                }
                setInterval(siteTime, 1000);
            </script>
            
            <br>
            
            <span id="icp"><img src="/medias/icp.png" style="vertical-align: text-bottom;" />
                <a href="http://www.beian.miit.gov.cn" target="_blank">皖ICP备18005729号-1</a>
            </span>
            
        </div>
        <div class="col s12 m4 l4 social-link social-statis">
    <a href="https://github.com/Annihilater" class="tooltipped" target="_blank" data-tooltip="访问我的GitHub" data-position="top" data-delay="50">
        <i class="fab fa-github"></i>
    </a>



    <a href="mailto:yanmiexingkong@gmail.com" class="tooltipped" target="_blank" data-tooltip="邮件联系我" data-position="top" data-delay="50">
        <i class="fas fa-envelope-open"></i>
    </a>





    <a href="https://twitter.com/" class="tooltipped" target="_blank" data-tooltip="关注我的Twitter: https://twitter.com/" data-position="top" data-delay="50">
        <i class="fab fa-twitter"></i>
    </a>







    <a href="https://www.zhihu.com/" class="tooltipped" target="_blank" data-tooltip="关注我的知乎: https://www.zhihu.com/" data-position="top" data-delay="50">
        <i class="fab fa-zhihu1">知</i>
    </a>



    <a href="/atom.xml" class="tooltipped" target="_blank" data-tooltip="RSS 订阅" data-position="top" data-delay="50">
        <i class="fas fa-rss"></i>
    </a>

</div>
    </div>
</footer>

<div class="progress-bar"></div>

    <!-- 搜索遮罩框 -->
<div id="searchModal" class="modal">
    <div class="modal-content">
        <div class="search-header">
            <span class="title"><i class="fas fa-search"></i>&nbsp;&nbsp;搜索</span>
            <input type="search" id="searchInput" name="s" placeholder="请输入搜索的关键字"
                   class="search-input">
        </div>
        <div id="searchResult"></div>
    </div>
</div>

<script src="/js/search.js"></script>
<script type="text/javascript">
$(function () {
    searchFunc("/" + "search.xml", 'searchInput', 'searchResult');
});
</script>
    <!-- 回到顶部按钮 -->
<div id="backTop" class="top-scroll">
    <a class="btn-floating btn-large waves-effect waves-light" href="#!">
        <i class="fas fa-arrow-up"></i>
    </a>
</div>


    <script src="/libs/materialize/materialize.min.js"></script>
    <script src="/libs/masonry/masonry.pkgd.min.js"></script>
    <script src="/libs/aos/aos.js"></script>
    <script src="/libs/scrollprogress/scrollProgress.min.js"></script>
    <script src="/libs/lightGallery/js/lightgallery-all.min.js"></script>
    <script src="/js/matery.js"></script>

    <!-- Global site tag (gtag.js) - Google Analytics -->


    <!-- Baidu Analytics -->

    <!-- Baidu Push -->

<script>
    (function () {
        var bp = document.createElement('script');
        var curProtocol = window.location.protocol.split(':')[0];
        if (curProtocol === 'https') {
            bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
        } else {
            bp.src = 'http://push.zhanzhang.baidu.com/push.js';
        }
        var s = document.getElementsByTagName("script")[0];
        s.parentNode.insertBefore(bp, s);
    })();
</script>

    
    <script src="/libs/others/clicklove.js" async="async"></script>
    
    
    <script async src="/libs/others/busuanzi.pure.mini.js"></script>
    

    

    

    

    

    
    <script type="text/javascript" src="/libs/background/ribbon-dynamic.js" async="async"></script>
    
    
    
    <script src="/libs/instantpage/instantpage.js" type="module"></script>
    

</body>

</html>
